package com.lumberer.pay2021.wx.init.loading;

import java.io.ByteArrayInputStream;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.Security;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import javax.annotation.PostConstruct;
import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import org.springframework.stereotype.Component;
import org.springframework.util.Base64Utils;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.lumberer.pay2021.util.WxRequest;
import com.lumberer.pay2021.wx.init.config.MerchantConfig;
import com.lumberer.pay2021.wx.init.config.RequestURL;

/**
 * 启动自装载服务
 * @author lumberer
 * @date 2021-09-24
 */
@Component
public class CertificateLoad {
	/**
	 * 初始化证书列表
	 */
	@PostConstruct
	public void init() {
		 System.err.println("获取微信证书列表中~~~");
		 Security.setProperty("crypto.policy", "unlimited");
		 Map<String, X509Certificate> certificateMap=refreshCertificate();
		 if(null==certificateMap){
			 System.err.println("微信证书获取失败~~~");
		 }
		 MerchantConfig.certificateMap.putAll(certificateMap);
	}

	/*
	 * 获取平台证书Map
	 * 	
	 * @return
	 */
	public static Map<String, X509Certificate> refreshCertificate() {
		// 获取平台证书json
		String jsonStr = WxRequest.wxHttpGet(RequestURL.CERTIFICATES);
		JSONObject jsonObject = JSONObject.parseObject(jsonStr);
		List<CertificateVO> certificateList = JSON.parseArray(jsonObject.getString("data"), CertificateVO.class);
		// 最新证书响应实体类
		CertificateVO newestCertificate = null;
		// 最新时间
		Date newestTime = null;
		for (CertificateVO certificate : certificateList) {
			SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
			// 如果最新时间是null
			if (newestTime == null) {
				newestCertificate = certificate;
				// 设置最新启用时间

				try {
					newestTime = formatter.parse(certificate.getEffective_time());
				} catch (ParseException e) {
					// TODO Auto-generated catch block
					System.err.println("转换异常：" + e.getMessage());
				}
			} else {
				try {
					Date effectiveTime = formatter.parse(certificate.getEffective_time());
					// 如果启用时间大于最新时间
					if (effectiveTime.getTime() > newestTime.getTime()) {
						// 更换最新证书响应实体类
						newestCertificate = certificate;
					}
				} catch (ParseException e) {
					// TODO Auto-generated catch block
					System.err.println("转换异常：" + e.getMessage());
				}

			}
		}

		CertificateVO.EncryptCertificate encryptCertificate = newestCertificate.getEncrypt_certificate();
		// 获取证书字公钥
		String publicKey = decryptResponseBody(encryptCertificate.getAssociated_data(), encryptCertificate.getNonce(),
				encryptCertificate.getCiphertext());
		CertificateFactory cf=null;
		try {
			cf = CertificateFactory.getInstance("X509");
		} catch (CertificateException e1) {
			// TODO Auto-generated catch block
			System.err.println(e1.getMessage());
		}

		// 获取证书
		ByteArrayInputStream inputStream = new ByteArrayInputStream(publicKey.getBytes(StandardCharsets.UTF_8));
		try {
			if(null==cf){
				return null;
			}
			X509Certificate certificate = (X509Certificate) cf.generateCertificate(inputStream);
			// 保存微信平台证书公钥
			Map<String, X509Certificate> certificateMap = new ConcurrentHashMap<>();
			// 清理HashMap
			certificateMap.clear();
			// 放入证书
			certificateMap.put(newestCertificate.getSerial_no(), certificate);
			return certificateMap;
		} catch (CertificateException e) {
			System.err.println(e.getMessage());
			return null;
		}
		
	}

	/**
	 * 用微信V3密钥解密响应体.
	 *
	 * @param associatedData
	 *            response.body.data[i].encrypt_certificate.associated_data
	 * @param nonce
	 *            response.body.data[i].encrypt_certificate.nonce
	 * @param ciphertext
	 *            response.body.data[i].encrypt_certificate.ciphertext
	 * @return the string
	 * @throws GeneralSecurityException
	 *             the general security exception
	 */
	public static String decryptResponseBody(String associatedData, String nonce, String ciphertext) {
		try {
			Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");

			SecretKeySpec key = new SecretKeySpec(MerchantConfig.apiV3.getBytes(StandardCharsets.UTF_8), "AES");
			
			GCMParameterSpec spec = new GCMParameterSpec(128, nonce.getBytes(StandardCharsets.UTF_8));

			cipher.init(Cipher.DECRYPT_MODE, key, spec);
			cipher.updateAAD(associatedData.getBytes(StandardCharsets.UTF_8));

			byte[] bytes = null;
			try {
				bytes = cipher.doFinal(Base64Utils.decodeFromString(ciphertext));
			} catch (GeneralSecurityException e) {
				System.err.println(e.getMessage());
			}
			return new String(bytes, StandardCharsets.UTF_8);
		} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
			System.err.println(e.getMessage());
			return null;
		} catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
			System.err.println(e.getMessage());
			return null;
		}
	}
}
